#ifndef _DEPOOLSET_H
#define _DEPOOLSET_H
/*-------------------------------------------------------------------------
 * drawElements Memory Pool Library
 * --------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Memory pool set class.
 *//*--------------------------------------------------------------------*/

#include "deDefs.h"
#include "deMemPool.h"
#include "dePoolArray.h"
#include "deInt32.h"

#include <string.h> /* memset() */

enum
{
	DE_SET_ELEMENTS_PER_SLOT	= 4
};

DE_BEGIN_EXTERN_C

void	dePoolSet_selfTest		(void);

DE_END_EXTERN_C

/*--------------------------------------------------------------------*//*!
 * \brief Declare a template pool set class interface.
 * \param TYPENAME	Type name of the declared set.
 * \param KEYTYPE	Type of the key.
 *
 * This macro declares the interface for a set. For the implementation of
 * the set, see DE_IMPLEMENT_POOL_HASH. Usually this macro is put into the
 * header file and the implementation macro is put in some .c file.
 *
 * \todo [petri] Detailed description.
 *
 * The functions for operating the set are:
 * \todo [petri] Figure out how to comment these in Doxygen-style.
 *
 * \code
 * Set*     Set_create            (deMemPool* pool);
 * int      Set_getNumElements    (const Set* array);
 * deBool   Set_exists            (const Set* array, Key key);
 * deBool   Set_insert            (Set* array, Key key);
 * void     Set_delete            (Set* array, Key key);
 * \endcode
*//*--------------------------------------------------------------------*/
#define DE_DECLARE_POOL_SET(TYPENAME, KEYTYPE)		\
\
typedef struct TYPENAME##Slot_s TYPENAME##Slot;    \
\
struct TYPENAME##Slot_s \
{    \
	int				numUsed; \
	TYPENAME##Slot*	nextSlot; \
	KEYTYPE			keys[DE_SET_ELEMENTS_PER_SLOT]; \
}; \
\
typedef struct TYPENAME##_s				\
{										\
	deMemPool*			pool;			\
	int					numElements;    \
										\
	int					slotTableSize;  \
	TYPENAME##Slot**	slotTable;		\
	TYPENAME##Slot*		slotFreeList;	\
} TYPENAME; /* NOLINT(TYPENAME) */		\
\
typedef struct TYPENAME##Iter_s \
{	\
	const TYPENAME*			hash;			\
	int						curSlotIndex;	\
	const TYPENAME##Slot*	curSlot;		\
	int						curElemIndex;	\
} TYPENAME##Iter;	\
\
TYPENAME*	TYPENAME##_create		(deMemPool* pool);							\
void		TYPENAME##_reset		(DE_PTR_TYPE(TYPENAME) set);				\
deBool		TYPENAME##_reserve		(DE_PTR_TYPE(TYPENAME) set, int capacity);	\
deBool		TYPENAME##_exists		(const TYPENAME* set, KEYTYPE key);			\
deBool		TYPENAME##_insert		(DE_PTR_TYPE(TYPENAME) set, KEYTYPE key);	\
void		TYPENAME##_delete		(DE_PTR_TYPE(TYPENAME) set, KEYTYPE key);	\
\
DE_INLINE int		TYPENAME##_getNumElements	(const TYPENAME* set)							DE_UNUSED_FUNCTION;	\
DE_INLINE void		TYPENAME##Iter_init			(const TYPENAME* hash, TYPENAME##Iter* iter)	DE_UNUSED_FUNCTION;	\
DE_INLINE deBool	TYPENAME##Iter_hasItem		(const TYPENAME##Iter* iter)					DE_UNUSED_FUNCTION;	\
DE_INLINE void		TYPENAME##Iter_next			(TYPENAME##Iter* iter)							DE_UNUSED_FUNCTION;	\
DE_INLINE KEYTYPE	TYPENAME##Iter_getKey		(const TYPENAME##Iter* iter)					DE_UNUSED_FUNCTION;	\
DE_INLINE deBool	TYPENAME##_safeInsert		(DE_PTR_TYPE(TYPENAME) set, KEYTYPE key)		DE_UNUSED_FUNCTION;	\
DE_INLINE void		TYPENAME##_safeDelete		(DE_PTR_TYPE(TYPENAME) set, KEYTYPE key)		DE_UNUSED_FUNCTION;	\
\
DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* set)    \
{    \
	return set->numElements;    \
}    \
\
DE_INLINE void TYPENAME##Iter_init (const TYPENAME* hash, TYPENAME##Iter* iter)    \
{	\
	iter->hash			= hash;		\
	iter->curSlotIndex	= 0;		\
	iter->curSlot		= DE_NULL;	\
	iter->curElemIndex	= 0;		\
	if (TYPENAME##_getNumElements(hash) > 0)		\
	{												\
		int slotTableSize	= hash->slotTableSize;	\
		int slotNdx			= 0;					\
		while (slotNdx < slotTableSize)				\
		{											\
			if (hash->slotTable[slotNdx])			\
				break;								\
			slotNdx++;								\
		}											\
		DE_ASSERT(slotNdx < slotTableSize);			\
		iter->curSlotIndex = slotNdx;				\
		iter->curSlot = hash->slotTable[slotNdx];	\
		DE_ASSERT(iter->curSlot);					\
	}	\
}	\
\
DE_INLINE deBool TYPENAME##Iter_hasItem	(const TYPENAME##Iter* iter)    \
{	\
	return (iter->curSlot != DE_NULL); \
}	\
\
DE_INLINE void TYPENAME##Iter_next (TYPENAME##Iter* iter)    \
{	\
	DE_ASSERT(TYPENAME##Iter_hasItem(iter));	\
	if (++iter->curElemIndex == iter->curSlot->numUsed)	\
	{													\
		iter->curElemIndex = 0;							\
		if (iter->curSlot->nextSlot)					\
		{												\
			iter->curSlot = iter->curSlot->nextSlot;	\
		}												\
		else											\
		{												\
			const TYPENAME*	hash			= iter->hash;			\
			int				curSlotIndex	= iter->curSlotIndex;	\
			int				slotTableSize	= hash->slotTableSize;	\
			while (++curSlotIndex < slotTableSize)		\
			{											\
				if (hash->slotTable[curSlotIndex])		\
					break;								\
			}											\
			iter->curSlotIndex = curSlotIndex;			\
			if (curSlotIndex < slotTableSize)					\
				iter->curSlot = hash->slotTable[curSlotIndex];	\
			else												\
				iter->curSlot = DE_NULL;						\
		}	\
	}	\
}	\
\
DE_INLINE KEYTYPE TYPENAME##Iter_getKey	(const TYPENAME##Iter* iter)    \
{	\
	DE_ASSERT(TYPENAME##Iter_hasItem(iter));	\
	return iter->curSlot->keys[iter->curElemIndex];	\
}	\
\
DE_INLINE deBool TYPENAME##_safeInsert (DE_PTR_TYPE(TYPENAME) set, KEYTYPE key)	\
{																	\
	DE_ASSERT(set);													\
	if (TYPENAME##_exists(set, key))								\
		return DE_TRUE;												\
	return TYPENAME##_insert(set, key);								\
}																	\
\
DE_INLINE void TYPENAME##_safeDelete (DE_PTR_TYPE(TYPENAME) set, KEYTYPE key)	\
{																	\
	DE_ASSERT(set);													\
	if (TYPENAME##_exists(set, key))								\
		TYPENAME##_delete(set, key);								\
}																	\
\
struct TYPENAME##Dummy_s { int dummy; }

/*--------------------------------------------------------------------*//*!
 * \brief Implement a template pool set class.
 * \param TYPENAME	Type name of the declared set.
 * \param KEYTYPE	Type of the key.
 * \param HASHFUNC	Function used for hashing the key.
 * \param CMPFUNC	Function used for exact matching of the keys.
 *
 * This macro has implements the set declared with DE_DECLARE_POOL_SET.
 * Usually this macro should be used from a .c file, since the macro expands
 * into multiple functions. The TYPENAME and KEYTYPE parameters
 * must match those of the declare macro.
*//*--------------------------------------------------------------------*/
#define DE_IMPLEMENT_POOL_SET(TYPENAME, KEYTYPE, HASHFUNC, CMPFUNC)		\
\
DE_PTR_TYPE(TYPENAME) TYPENAME##_create (deMemPool* pool)    \
{   \
	/* Alloc struct. */ \
	DE_PTR_TYPE(TYPENAME) set = DE_POOL_NEW(pool, TYPENAME); \
	if (!set) \
		return DE_NULL; \
\
	/* Init array. */ \
	memset(set, 0, sizeof(TYPENAME)); \
	set->pool = pool; \
\
	return set; \
} \
\
void TYPENAME##_reset (DE_PTR_TYPE(TYPENAME) set)    \
{   \
	int slotNdx; \
	for (slotNdx = 0; slotNdx < set->slotTableSize; slotNdx++)	\
	{	\
		TYPENAME##Slot* slot = set->slotTable[slotNdx]; \
		while (slot) \
		{ \
			TYPENAME##Slot*	nextSlot = slot->nextSlot;	\
			slot->nextSlot = set->slotFreeList;		\
			set->slotFreeList = slot;	\
			slot->numUsed = 0;			\
			slot = nextSlot;			\
		}	\
		set->slotTable[slotNdx] = DE_NULL; \
	}	\
	set->numElements = 0; \
}	\
\
TYPENAME##Slot* TYPENAME##_allocSlot (DE_PTR_TYPE(TYPENAME) set)    \
{   \
	TYPENAME##Slot* slot; \
	if (set->slotFreeList) \
	{ \
		slot = set->slotFreeList; \
		set->slotFreeList = set->slotFreeList->nextSlot; \
	} \
	else \
		slot = (TYPENAME##Slot*)deMemPool_alloc(set->pool, sizeof(TYPENAME##Slot) * DE_SET_ELEMENTS_PER_SLOT); \
\
	if (slot) \
	{ \
		slot->nextSlot = DE_NULL; \
		slot->numUsed = 0; \
	} \
\
	return slot; \
} \
\
deBool TYPENAME##_rehash (DE_PTR_TYPE(TYPENAME) set, int newSlotTableSize)    \
{    \
	DE_ASSERT(deIsPowerOfTwo32(newSlotTableSize) && newSlotTableSize > 0); \
	if (newSlotTableSize > set->slotTableSize)    \
	{ \
		TYPENAME##Slot**	oldSlotTable = set->slotTable; \
		TYPENAME##Slot**	newSlotTable = (TYPENAME##Slot**)deMemPool_alloc(set->pool, sizeof(TYPENAME##Slot*) * (size_t)newSlotTableSize); \
		int					oldSlotTableSize = set->slotTableSize; \
		int					slotNdx; \
\
		if (!newSlotTable) \
			return DE_FALSE; \
\
		for (slotNdx = 0; slotNdx < oldSlotTableSize; slotNdx++) \
			newSlotTable[slotNdx] = oldSlotTable[slotNdx]; \
\
		for (slotNdx = oldSlotTableSize; slotNdx < newSlotTableSize; slotNdx++) \
			newSlotTable[slotNdx] = DE_NULL; \
\
		set->slotTableSize		= newSlotTableSize; \
		set->slotTable			= newSlotTable; \
\
		for (slotNdx = 0; slotNdx < oldSlotTableSize; slotNdx++) \
		{ \
			TYPENAME##Slot* slot = oldSlotTable[slotNdx]; \
			newSlotTable[slotNdx] = DE_NULL; \
			while (slot) \
			{ \
				int elemNdx; \
				for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \
				{ \
					set->numElements--; \
					if (!TYPENAME##_insert(set, slot->keys[elemNdx])) \
						return DE_FALSE; \
				} \
				slot = slot->nextSlot; \
			} \
		} \
	} \
\
	return DE_TRUE;    \
}    \
\
deBool TYPENAME##_exists (const TYPENAME* set, KEYTYPE key)    \
{    \
	if (set->numElements > 0) \
	{	\
		int				slotNdx	= (int)(HASHFUNC(key) & (deUint32)(set->slotTableSize - 1)); \
		TYPENAME##Slot*	slot	= set->slotTable[slotNdx]; \
		DE_ASSERT(deInBounds32(slotNdx, 0, set->slotTableSize)); \
	\
		while (slot) \
		{ \
			int elemNdx; \
			for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \
			{ \
				if (CMPFUNC(slot->keys[elemNdx], key)) \
					return DE_TRUE; \
			} \
			slot = slot->nextSlot; \
		} \
	} \
\
	return DE_FALSE; \
}    \
\
deBool TYPENAME##_insert (DE_PTR_TYPE(TYPENAME) set, KEYTYPE key)    \
{    \
	int				slotNdx; \
	TYPENAME##Slot*	slot; \
\
	DE_ASSERT(set); \
	DE_ASSERT(!TYPENAME##_exists(set, key)); \
\
	if ((set->numElements + 1) >= set->slotTableSize * DE_SET_ELEMENTS_PER_SLOT) \
		if (!TYPENAME##_rehash(set, deMax32(4, 2*set->slotTableSize))) \
			return DE_FALSE; \
\
	slotNdx	= (int)(HASHFUNC(key) & (deUint32)(set->slotTableSize - 1)); \
	DE_ASSERT(slotNdx >= 0 && slotNdx < set->slotTableSize); \
	slot	= set->slotTable[slotNdx]; \
\
	if (!slot) \
	{ \
		slot = TYPENAME##_allocSlot(set); \
		if (!slot) return DE_FALSE; \
		set->slotTable[slotNdx] = slot; \
	} \
\
	for (;;) \
	{ \
		if (slot->numUsed == DE_SET_ELEMENTS_PER_SLOT) \
		{ \
			if (slot->nextSlot) \
				slot = slot->nextSlot; \
			else \
			{ \
				TYPENAME##Slot* nextSlot = TYPENAME##_allocSlot(set); \
				if (!nextSlot) return DE_FALSE; \
				slot->nextSlot = nextSlot; \
				slot = nextSlot; \
			} \
		} \
		else \
		{ \
			slot->keys[slot->numUsed]	= key; \
			slot->numUsed++; \
			set->numElements++; \
			return DE_TRUE; \
		} \
	} \
} \
\
void TYPENAME##_delete (DE_PTR_TYPE(TYPENAME) set, KEYTYPE key)    \
{    \
	int				slotNdx; \
	TYPENAME##Slot*	slot; \
	TYPENAME##Slot*	prevSlot = DE_NULL; \
\
	DE_ASSERT(set->numElements > 0); \
	slotNdx	= (int)(HASHFUNC(key) & (deUint32)(set->slotTableSize - 1)); \
	DE_ASSERT(slotNdx >= 0 && slotNdx < set->slotTableSize); \
	slot	= set->slotTable[slotNdx]; \
	DE_ASSERT(slot); \
\
	for (;;) \
	{ \
		int elemNdx; \
		DE_ASSERT(slot->numUsed > 0); \
		for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \
		{ \
			if (CMPFUNC(key, slot->keys[elemNdx])) \
			{ \
				TYPENAME##Slot*	lastSlot = slot; \
				while (lastSlot->nextSlot) \
				{ \
					prevSlot = lastSlot; \
					lastSlot = lastSlot->nextSlot; \
				} \
\
				slot->keys[elemNdx] = lastSlot->keys[lastSlot->numUsed-1]; \
				lastSlot->numUsed--; \
\
				if (lastSlot->numUsed == 0) \
				{ \
					if (prevSlot) \
						prevSlot->nextSlot = DE_NULL; \
					else \
						set->slotTable[slotNdx] = DE_NULL; \
\
					lastSlot->nextSlot = set->slotFreeList; \
					set->slotFreeList = lastSlot; \
				} \
\
				set->numElements--; \
				return; \
			} \
		} \
\
		prevSlot = slot; \
		slot = slot->nextSlot; \
		DE_ASSERT(slot); \
	} \
}    \
\
struct TYPENAME##Dummy2_s { int dummy; }

/* Copy-to-array templates. */

#define DE_DECLARE_POOL_SET_TO_ARRAY(SETTYPENAME, ARRAYTYPENAME)		\
	deBool SETTYPENAME##_copyToArray(const SETTYPENAME* set, DE_PTR_TYPE(ARRAYTYPENAME) array);	\
	struct SETTYPENAME##_##ARRAYTYPENAME##_declare_dummy { int dummy; }

#define DE_IMPLEMENT_POOL_SET_TO_ARRAY(SETTYPENAME, ARRAYTYPENAME)		\
	deBool SETTYPENAME##_copyToArray(const SETTYPENAME* set, DE_PTR_TYPE(ARRAYTYPENAME) array)	\
	{	\
		int numElements	= set->numElements;	\
		int arrayNdx	= 0;	\
		int slotNdx;	\
\
		if (!ARRAYTYPENAME##_setSize(array, numElements))	\
			return DE_FALSE;	\
\
		for (slotNdx = 0; slotNdx < set->slotTableSize; slotNdx++) \
		{ \
			const SETTYPENAME##Slot* slot = set->slotTable[slotNdx]; \
			while (slot) \
			{ \
				int elemNdx; \
				for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \
					ARRAYTYPENAME##_set(array, arrayNdx++, slot->keys[elemNdx]); \
				slot = slot->nextSlot; \
			} \
		}	\
		DE_ASSERT(arrayNdx == numElements);	\
		return DE_TRUE;	\
	}	\
	struct SETTYPENAME##_##ARRAYTYPENAME##_implement_dummy { int dummy; }

/*--------------------------------------------------------------------*//*!
 * \brief Declare set-wise operations for a set template.
 * \param TYPENAME	Type name of the declared set.
 * \param KEYTYPE	Type of the key.
 *
 * This macro declares union and intersection operations for a set.
 * For implementation see DE_IMPLEMENT_POOL_SET_UNION_INTERSECT.
 *
 * \todo [petri] Detailed description.
 *
 * The functions for operating the set are:
 * \todo [petri] Figure out how to comment these in Doxygen-style.
 *
 * \code
 * deBool	Set_union				(Set* to, const Set* a, const Set* b);
 * deBool	Set_unionInplace		(Set* a, const Set* b);
 * deBool	Set_intersect			(Set* to, const Set* a, const Set* b);
 * void		Set_intersectInplace	(Set* a, const Set* b);
 * deBool   Set_difference			(Set* to, const Set* a, const Set* b);
 * void     Set_differenceInplace	(Set* a, const Set* b);
 * \endcode
*//*--------------------------------------------------------------------*/
#define DE_DECLARE_POOL_SET_SETWISE_OPERATIONS(TYPENAME)											\
	deBool TYPENAME##_union (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b);		\
	deBool TYPENAME##_unionInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b);					\
	deBool TYPENAME##_intersect (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b);	\
	void TYPENAME##_intersectInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b);					\
	deBool TYPENAME##_difference (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b);	\
	void TYPENAME##_differenceInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b);					\
	struct TYPENAME##SetwiseDeclareDummy_s { int dummy; }

#define DE_IMPLEMENT_POOL_SET_SETWISE_OPERATIONS(TYPENAME, KEYTYPE)	\
deBool TYPENAME##_union (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b)	\
{	\
	TYPENAME##_reset(to);	\
	if (!TYPENAME##_unionInplace(to, a))	\
		return DE_FALSE;	\
	if (!TYPENAME##_unionInplace(to, b))	\
		return DE_FALSE;	\
	return DE_TRUE;	\
}	\
\
deBool TYPENAME##_unionInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b)	\
{	\
	TYPENAME##Iter iter;	\
	for (TYPENAME##Iter_init(b, &iter);	\
		 TYPENAME##Iter_hasItem(&iter);	\
		 TYPENAME##Iter_next(&iter))	\
	{	\
		KEYTYPE key = TYPENAME##Iter_getKey(&iter);	\
		if (!TYPENAME##_exists(a, key))	\
		{	\
			if (!TYPENAME##_insert(a, key))	\
				return DE_FALSE;	\
		}	\
	}	\
	return DE_TRUE;	\
}	\
\
deBool TYPENAME##_intersect (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b)	\
{	\
	TYPENAME##Iter iter;	\
	TYPENAME##_reset(to);	\
	for (TYPENAME##Iter_init(a, &iter);	\
		 TYPENAME##Iter_hasItem(&iter);	\
		 TYPENAME##Iter_next(&iter))	\
	{	\
		KEYTYPE key = TYPENAME##Iter_getKey(&iter);	\
		if (TYPENAME##_exists(b, key))	\
		{	\
			if (!TYPENAME##_insert(to, key))	\
				return DE_FALSE;	\
		}	\
	}	\
	return DE_TRUE;	\
}	\
\
void TYPENAME##_intersectInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b)	\
{	\
	DE_UNREF(a && b);	\
	DE_FATAL("Not implemented.");	\
}	\
\
deBool TYPENAME##_difference (DE_PTR_TYPE(TYPENAME) to, const TYPENAME* a, const TYPENAME* b)	\
{	\
	TYPENAME##Iter iter;	\
	TYPENAME##_reset(to);	\
	for (TYPENAME##Iter_init(a, &iter);	\
		 TYPENAME##Iter_hasItem(&iter);	\
		 TYPENAME##Iter_next(&iter))	\
	{	\
		KEYTYPE key = TYPENAME##Iter_getKey(&iter);	\
		if (!TYPENAME##_exists(b, key))	\
		{	\
			if (!TYPENAME##_insert(to, key))	\
				return DE_FALSE;	\
		}	\
	}	\
	return DE_TRUE;	\
}	\
\
void TYPENAME##_differenceInplace (DE_PTR_TYPE(TYPENAME) a, const TYPENAME* b)	\
{	\
	TYPENAME##Iter iter;	\
	for (TYPENAME##Iter_init(b, &iter);	\
		 TYPENAME##Iter_hasItem(&iter);	\
		 TYPENAME##Iter_next(&iter))	\
	{	\
		KEYTYPE key = TYPENAME##Iter_getKey(&iter);	\
		if (TYPENAME##_exists(a, key))	\
			TYPENAME##_delete(a, key);	\
	}	\
}	\
\
struct TYPENAME##UnionIntersectImplementDummy_s { int dummy; }

#endif /* _DEPOOLSET_H */
